﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Scream.AI;
using System.Xml.Serialization;
using System.IO;

namespace AITest {
    public class Program {
        public enum LocationType { @Home = 1, @Work = 2, @Vocation = 3, @Holidays = 4, Driving2Job = 5, Driving2Home = 6 }

        public static string GetStringFromStream(Stream stream) {
            // Create a stream reader.
            using (StreamReader reader = new StreamReader(stream)) {
                // Just read to the end.
                return reader.ReadToEnd();
            }
        }

        static void Main(string[] args) {

            Human julia = new Human();
            julia.BirthDate = new DateTime(1977, 9, 29);
            julia.Name = "Julia";

            Human aleksandr = new Human();
            aleksandr.BirthDate = new DateTime(1978, 4, 9);
            aleksandr.Name = "Aleksandr";

            Human richard = new Human();
            richard.BirthDate = new DateTime(2004, 10, 13);
            richard.Name = "Richard";
            richard.Mother = julia;
            richard.Father = aleksandr;

            AI guru = new AI();



            guru.DataStorage.StoreObject("Richard", richard);
            guru.DataStorage.StoreObject("Aleksandr", aleksandr);
            guru.DataStorage.StoreObject("Julia", julia);
            guru.DataStorage.StoreObject("Now", DateTime.Now);
            guru.DataStorage.StoreObject("LocationType", System.Enum.GetValues(typeof(LocationType)));
            guru.DataStorage.StoreObject("CurrentLocation", (int)LocationType.Work);
            guru.DataStorage.StoreObject("HoursWorked", 7.152F);
            guru.DataStorage.StoreObject("RegistredAtJob", DateTime.Now.Date.Add(new TimeSpan(8, 30, 0)));
            guru.DataStorage.StoreObject("LastEatTime", DateTime.Now.Date.Add(new TimeSpan(7, 30, 0)));
            // guru.DataStorage.StoreObject("HasImportantTask", true);

            RuleEntity rule0 = new RuleEntity("Richard's birthdate", "[Now.Day] == [Richard.BirthDate.Day] && [Now.Month] == [Richard.BirthDate.Month]", "Today is Richard's birthdate");
            RuleEntity rule1 = new RuleEntity("Richard's birthdate party", "[Now.Hour] >= 17 && [Now.Day] == [Richard.BirthDate.Day] && [Now.Month] == [Richard.BirthDate.Month]", "Lets start Richard's party");
            RuleEntity rule2 = new RuleEntity("Is parents?", "([Richard.Mother.Name] == [Julia.Name]) || ([Richard.Father.Name] == [Aleksandr.Name])", "One of this persons is Richards parent");

            guru.RulesStorage.Rules.Add(rule0);
            guru.RulesStorage.Rules.Add(rule1);
            guru.RulesStorage.Rules.Add(rule2);

            ImportanceRuleEntity importanceRule1 = new ImportanceRuleEntity("Job", "[CurrentLocation] == 2", "Do your job", @"1.0F - ([HoursWorked] / 8.0F)");
            ScheduleEntity scheduleWorkDay = new ScheduleEntity();
            ScheduleEntity.SpanEntity span1 = new ScheduleEntity.SpanEntity(ScheduleEntity.SpanEntity.SpanType.EveryWorkDay, new TimeSpan(8, 30, 0), new TimeSpan(8, 0, 0), new TimeSpan(1, 0, 0), new TimeSpan(4, 0, 0));
            scheduleWorkDay.Spans.Add(span1);
            ScheduleRuleEntity scheduleRule1 = new ScheduleRuleEntity(scheduleWorkDay, new ImportanceEntity(importanceRule1));

            ImportanceRuleEntity importanceRule2 = new ImportanceRuleEntity("Rest", "[CurrentLocation] == 1", "You must rest", @"[HoursWorked] / 8.0F");
            ScheduleEntity scheduleAlways = new ScheduleEntity();
            scheduleAlways.RunAlways = true;
            ScheduleRuleEntity scheduleRule2 = new ScheduleRuleEntity(scheduleAlways, new ImportanceEntity(importanceRule2));

            ImportanceRuleEntity importanceRule3 = new ImportanceRuleEntity("Go2home", "[CurrentLocation] == 2", "You must go home", @"([HoursWorked] - 8.0F) / 4.0F");
            ScheduleRuleEntity scheduleRule3 = new ScheduleRuleEntity(scheduleAlways, new ImportanceEntity(importanceRule3));

            ImportanceRuleEntity importanceRule4 = new ImportanceRuleEntity("Go2Work", "[CurrentLocation] != 2", "You must hurry to work", @"[HoursWorked] / 8.0F");
            ScheduleRuleEntity scheduleRule4 = new ScheduleRuleEntity(scheduleWorkDay, new ImportanceEntity(importanceRule4));

            ImportanceRuleEntity importanceRule5 = new ImportanceRuleEntity("Eat", "([Now] - [LastEatTime]).TotalHours > 4", "Go and eat something", @"((float)([Now] - [LastEatTime]).TotalHours - 4.0F) / 6.0F");
            ScheduleRuleEntity scheduleRule5 = new ScheduleRuleEntity(scheduleAlways, new ImportanceEntity(importanceRule5));

            guru.ScheduleRuleStorage.ScheduleRules.Add(scheduleRule1);
            guru.ScheduleRuleStorage.ScheduleRules.Add(scheduleRule2);
            guru.ScheduleRuleStorage.ScheduleRules.Add(scheduleRule3);
            guru.ScheduleRuleStorage.ScheduleRules.Add(scheduleRule4);
            guru.ScheduleRuleStorage.ScheduleRules.Add(scheduleRule5);

            // scheduleRule1.Importance.ImportanceRule = new RuleEntity("Are you hungry?", "(Now - LastEatTime).TotalHours / 8.0", "You must eat");


            while (true) {
                DateTime start = DateTime.Now;
                Console.WriteLine("<!-- Data start -->");
                foreach (string key in guru.DataStorage.Objects.Keys) {
                    Console.WriteLine("<!-- " + key + " start -->");
                    object obj = guru.DataStorage.Objects[key];
                    try {

                        XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType());
                        StringWriter sw = new StringWriter();
                        xmlSerializer.Serialize(sw, obj);
                        string result = sw.ToString();
                        // string result = GetStringFromStream(memoryStream);
                        if (result == null || result.Trim() == string.Empty) {
                            Console.WriteLine(obj.ToString());
                        }
                        else {
                            Console.WriteLine("\t" + result.Replace("\n", "\n\t"));
                        }
                        // memoryStream.Close();
                    }
                    catch (Exception ex) {
                        Console.WriteLine(ex.Message);
                        Console.WriteLine(obj.ToString());
                    }
                    Console.WriteLine("<!-- " + key + " end -->");
                }
                Console.WriteLine("<!-- Data end -->");
                guru.MakeDecision();
                TimeSpan span = DateTime.Now - start;

                // DateTime start = DateTime.Now;
                // TimeSpan span = DateTime.Now - start;
                Console.WriteLine("MakeDecision takes " + span.TotalMilliseconds.ToString() + " miliseconds");

                start = DateTime.Now;
                guru.DecideActions();
                span = DateTime.Now - start;

                // DateTime start = DateTime.Now;
                // TimeSpan span = DateTime.Now - start;
                Console.WriteLine("DecideActions takes " + span.TotalMilliseconds.ToString() + " miliseconds");

                Console.WriteLine("<!-- Decisions start -->");
                foreach (SolutionEntity solution in guru.Decision.Decisions) {
                    Console.WriteLine(solution.Description);
                }
                Console.WriteLine("<!-- Decisions end -->");

                Console.WriteLine("<!-- Actions start -->");
                foreach (ScheduleRuleEntity scheduleRule in guru.Decision.TrueScheduleRules) {
                    Console.WriteLine(scheduleRule.RuleImportance.ImportanceRule.Rule.Name + "|" + scheduleRule.RuleImportance.Importance.ToString());
                }
                Console.WriteLine("<!-- Actions end -->");

                if (guru.Decision.Solution != null) {
                    Console.WriteLine("<!-- Solution start -->");
                    Console.WriteLine(guru.Decision.Solution.Description);
                    Console.WriteLine("<!-- Solution end -->");
                }

                string line = Console.ReadLine();
                if (line != null) {
                    if (line.StartsWith("Now = ")) {
                        string t = line.Replace("Now = ", "");
                        try {
                            DateTime date = DateTime.Parse(t);
                            DateTime registredAtJob = (DateTime)guru.DataStorage.GetObject("RegistredAtJob");
                            float hoursWorked = (float)(date - registredAtJob).TotalHours;
                            guru.DataStorage.StoreObject("Now", date);
                            guru.DataStorage.StoreObject("HoursWorked", hoursWorked);
                        }
                        catch (Exception ex) {
                            Console.WriteLine(ex.Message);
                        }
                    }

                    if (line.StartsWith("LastEatTime = ")) {
                        string t = line.Replace("LastEatTime = ", "");
                        try {
                            DateTime date = DateTime.Parse(t);
                            guru.DataStorage.StoreObject("LastEatTime", date);
                        }
                        catch (Exception ex) {
                            Console.WriteLine(ex.Message);
                        }
                    }

                    if (line.StartsWith("Time = ")) {
                        object obj = guru.DataStorage.GetObject("Now");

                        string t = (obj == null ? DateTime.Now.Date.ToShortDateString() : ((DateTime)obj).Date.ToShortDateString()) + " " + line.Replace("Time = ", "");
                        try {
                            DateTime date = DateTime.Parse(t);
                            guru.DataStorage.StoreObject("Now", date);
                        }
                        catch (Exception ex) {
                            Console.WriteLine(ex.Message);
                        }
                    }

                    if (line.StartsWith("HasImportantTask = ")) {
                        string t = line.Replace("HasImportantTask = ", "").Trim().ToLower();
                        if (t == "true" || t == "false") {
                            guru.DataStorage.StoreObject("HasImportantTask", t == "true" ? true : false);
                        }
                    }

                    if (line.EndsWith("?")) {
                        string objName = line.Length > 0 ? line.Substring(0, line.Length - 1) : "";
                        try {
                            object obj = guru.DataStorage.GetObject(objName);

                            //if (obj.GetType() == typeof(DateTime)) {
                            //    Console.WriteLine("Minutes = " + ((DateTime)obj).Minute);
                            //}

                            if (obj != null) {
                                Console.WriteLine(obj.ToString());
                            }
                            else {
                                Console.WriteLine("obj not found or is null");
                            }
                        }
                        catch (Exception ex) {
                            Console.WriteLine(ex.Message);
                        }
                    }
                    else if (line.ToLower().Contains("exit")) {
                        break;
                    }
                }
            }

            Console.WriteLine("goodbye!");
        }

        public class Human {
            public DateTime BirthDate { get; set; }
            public string Name { get; set; }
            public object Head { get; set; }
            public Human Mother { get; set; }
            public Human Father { get; set; }

            public string GetName() {
                return Name;
            }
        }
    }
}
